<?php

namespace app\common\service;

use app\common\model\OrderCommon;
use EasyWeChat\Factory;
use GuzzleHttp\Client;
use think\facade\Cache;

class WechatOfficialService extends PlatformBaseService
{
    public $platform = PlatFormFactory::OFFICIAL;
    /**
     * @var \GuzzleHttp\Client
     */
    protected $client;
    protected $appid;
    protected $secret;

    /**
     *
     */
    public function __construct()
    {
        parent::__construct();

        $this->appid = $this->config['appid'];
        $this->secret = $this->config['secret'];
        $this->client = new Client([
            'base_uri' => 'https://api.weixin.qq.com'
        ]);
    }

    /**
     * @param \app\common\model\OrderCommon $model
     * @param string $tradeType
     * @return array|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function createOrder(OrderCommon $model, string $tradeType = 'JSAPI')
    {
        $service = new WechatService($this->appid, url('PayNotify/official')->suffix(false)->domain(true)->build());

        return $service->createOrder($model, $tradeType);
    }

    /**
     * @param \app\common\model\OrderCommon $model
     * @return void
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function refundOrder(OrderCommon $model)
    {
        (new WechatService())->refundOrder($model);
    }

    /**
     * @param string $code
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function parseCode(string $code): array
    {
        $resp = $this->client->get('/sns/oauth2/access_token', [
            'query' => [
                'appid'      => $this->appid,
                'secret'     => $this->secret,
                'code'       => $code,
                'grant_type' => 'authorization_code',
            ]
        ]);

        return $this->checkResp($resp);
    }

    /**
     * @param array $APIs
     * @param $url
     * @param bool $debug
     * @return array
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function buildConfig(array $APIs, $url, bool $debug = false): array
    {
        $config = [
            'app_id' => $this->appid,
            'secret' => $this->secret,
        ];

        $app = Factory::officialAccount($config);

        $app->jssdk->setUrl($url);

        return $app->jssdk->buildConfig($APIs, $debug, false, false);
    }

    /**
     * @return mixed
     * @throws \Throwable
     */
    private function getJsapiTicket(): array
    {
        return Cache::remember('jsapi_ticket', function () {
            $accessToken = $this->getAccessToken();

            $resp = $this->client->get('/cgi-bin/ticket/getticket', [
                'query' => [
                    'access_token' => $accessToken,
                    'type'         => 'jsapi',
                ]
            ]);

            return $this->checkResp($resp);
        }, 7200);
    }

    /**
     * @return mixed
     * @throws \Throwable
     */
    private function getAccessToken(): string
    {
        return Cache::remember('official_token', function () {
            $resp = $this->client->get('/cgi-bin/token', [
                'query' => [
                    'grant_type' => 'client_credential',
                    'appid'      => $this->appid,
                    'secret'     => $this->secret,
                ]
            ]);

            return $this->checkResp($resp)['access_token'];
        }, 7200);
    }

    /**
     * @param $resp
     * @return mixed
     */
    private function checkResp($resp)
    {
        $content = json_decode($resp->getBody()->getContents(), true);
        if (isset($content['errcode']) && $content['errcode'] > 0) {
            abort(400, $content['errmsg']);
        }
        return $content;
    }
}